package dd.lo.chapter10;

import dd.lo.util.ImageUtils;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Exercise 5
 * Create an outline of an object. Take a picture of a scene. Then, without moving the camera, put a coffee cup in the scene and take a second picture. Load these images and convert both to 8-bit grayscale images.
 * Take the absolute value of their difference. Display the result, which should look like a noisy mask of a coffee mug.
 * Do a binary threshold of the resulting image using a level that preserves most of the coffee mug but removes some of the noise. Display the result. The “on” values should be set to 255.
 * Do a cv::MOP_OPEN on the image to further clean up noise.
 * Using the erosion operator and logical XOR function, turn the mask of the coffee cup image into an outline of the coffee cup (only the edge pixels remaining).
 */
public class Exercise5 extends Application {

    private static final int DEFAULT_WIDTH = 1080;
    private static final int DEFAULT_HEIGHT = 768;

    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        launch(args);
    }

    private ImageView imageView;

    private Mat srcImg1;

    private Mat srcImg2;

    private int threshold = 70;

    private int iteration = 4;

    private void outlineObject() {
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() {
                Mat dst = new Mat(), temp = new Mat();
                //Take the absolute value of their difference.
                Core.absdiff(srcImg1, srcImg2, temp);
                Imgproc.threshold(temp, temp, threshold, 255, Imgproc.THRESH_BINARY);
                Imgproc.morphologyEx(temp, temp, Imgproc.MORPH_OPEN, new Mat(), new Point(-1, -1), iteration);
                Imgproc.dilate(temp, temp, new Mat(), new Point(-1, -1), iteration);
                Core.bitwise_xor(srcImg1, temp, dst);
                //调整图片大小以用于展示
                ImageUtils.fitImgSize(dst, DEFAULT_WIDTH, DEFAULT_HEIGHT);
                MatOfByte buffer = new MatOfByte();
                Imgcodecs.imencode(".jpeg", dst, buffer);
                Platform.runLater(() -> imageView.setImage(new Image(new ByteArrayInputStream(buffer.toArray()))));
                return null;
            }
        };
        new Thread(task).start();
    }

    @Override
    public void start(Stage stage) {
        Text text1 = new Text();
        Button image1Chooser = new Button("选择第一张图片");
        image1Chooser.setOnMouseClicked(e -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("请选择图片");
            fileChooser.setInitialDirectory(new File(System.getProperty("user.home") + "/Downloads")
            );
            fileChooser.getExtensionFilters().addAll(
//                    new FileChooser.ExtensionFilter("All Images", "*.*"),
                    new FileChooser.ExtensionFilter("JPEG", "*.jpeg"),
                    new FileChooser.ExtensionFilter("WEBP", "*.webp"),
                    new FileChooser.ExtensionFilter("PNG", "*.png"),
                    new FileChooser.ExtensionFilter("JPG", "*.jpg"),
                    new FileChooser.ExtensionFilter("GIF", "*.gif"),
                    new FileChooser.ExtensionFilter("BMP", "*.bmp")
            );
            File imgFile = fileChooser.showOpenDialog(stage);
            if (null == imgFile) {
                System.out.println("用户取消选中文件");
                return;
            }
            //读取第一张图片
            srcImg1 = Imgcodecs.imread(imgFile.getAbsolutePath(), Imgcodecs.IMREAD_GRAYSCALE);
            if (srcImg1.empty()) {
                throw new RuntimeException("图片读取失败");
            }
            srcImg1.convertTo(srcImg1, CvType.CV_8U);
            text1.setText("成功导入第一张图片");
        });
        Text text2 = new Text();
        Button image2Chooser = new Button("选择第二张图片");
        image2Chooser.setOnMouseClicked(e -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("请选择图片");
            fileChooser.setInitialDirectory(new File(System.getProperty("user.home") + "/Downloads")
            );
            fileChooser.getExtensionFilters().addAll(
//                    new FileChooser.ExtensionFilter("All Images", "*.*"),
                    new FileChooser.ExtensionFilter("JPEG", "*.jpeg"),
                    new FileChooser.ExtensionFilter("WEBP", "*.webp"),
                    new FileChooser.ExtensionFilter("PNG", "*.png"),
                    new FileChooser.ExtensionFilter("JPG", "*.jpg"),
                    new FileChooser.ExtensionFilter("GIF", "*.gif"),
                    new FileChooser.ExtensionFilter("BMP", "*.bmp")
            );
            File imgFile = fileChooser.showOpenDialog(stage);
            if (null == imgFile) {
                System.out.println("用户取消选中文件");
                return;
            }
            //读取第二张图片
            srcImg2 = Imgcodecs.imread(imgFile.getAbsolutePath(), Imgcodecs.IMREAD_GRAYSCALE);
            if (srcImg2.empty()) {
                throw new RuntimeException("图片读取失败");
            }
            srcImg2.convertTo(srcImg2, CvType.CV_8U);
            text2.setText("成功导入第二张图片");
            outlineObject();
        });
        //Iteration indicator
        Text iterationText = new Text("iteration = " + iteration);
        Button minuxButton = new Button("-");
        minuxButton.setOnMouseClicked(e -> {
            --iteration;
            if (iteration < 1) iteration = 1;
            iterationText.setText("iteration = " + iteration);
            outlineObject();
        });
        Button addButton = new Button("+");
        addButton.setOnMouseClicked(e -> {
            ++iteration;
            if (iteration > 10) iteration = 10;
            iterationText.setText("iteration = " + iteration);
            outlineObject();
        });
        FlowPane bottomControl = new FlowPane();
        bottomControl.getChildren().addAll(image1Chooser, text1, image2Chooser, text2, minuxButton, iterationText, addButton);
        bottomControl.setPrefHeight(50);
        BorderPane root = new BorderPane();
        imageView = new ImageView();
        root.setCenter(imageView);
        root.setBottom(bottomControl);
        Scene scene = new Scene(root, DEFAULT_WIDTH, DEFAULT_HEIGHT + 50);
        stage.setScene(scene);
        stage.setTitle("Exercise5-An outline of an object");
        stage.setResizable(false);
        stage.show();
    }
}
